#include <service.h>
#include <graphic.h>
#include <service/view.h>
#include <memory.h>
#include <time.h>
#include <io.h>
#include <debug.h>

#include <std/stdio.h>

PRIVATE viewctl_t viewctl;
PRIVATE view_t* bg;

PRIVATE void viewctl_init(viewctl_t* ctl,graph_info_t info,int views);
PRIVATE view_t* view_alloc(viewctl_t* ctl);
PRIVATE void view_init(view_t* view,graph_info_t info,pid_t holder);
PRIVATE void view_remove(view_t* view);
PRIVATE void view_updown(view_t* view,int height);
PRIVATE void view_slide(view_t* view,position_t pos);
PRIVATE void view_reflush(view_t* view,int x0,int y0,int x1,int y1);
PRIVATE void view_reflushsub(viewctl_t* ctl,int x0,int y0,int x1,int y1,int h0,int h1);
PRIVATE void view_reflushmap(viewctl_t* ctl,int x0,int y0,int x1,int y1,int h0);

PRIVATE void time_thread()
{
    PRIVATE pixel_t col ={255,255,255,0};
    char s[64];
    char* time_style = "%04x/%02x/%02x %02x:%02x:%02x";
    time_t time;
    get_time(&time);
    unsigned int time_x0 = 0;
    unsigned int time_y0 = 0;
    position_t pos = {0,0};

    col.alpha = 255;
    sprintf(s,time_style,0,0,0,0,0,0,0);
    pr_ttf_str(&bg->info,&pos,col,s,12.0);
    pr_ttf_str(&bg->info,&pos,col,"\n",12.0);
    time_x0 = bg->info.horizontal_resolution - pos.x - 20;
    time_y0 = bg->info.vertical_resolution - (70 + pos.y) / 2;
    col.alpha = 0;

    view_t* view_time = view_alloc(&viewctl);
    graph_info_t info;
    info.frame_buffer_base = (uintptr_t)KADDR_P2V(pmalloc(pos.x * pos.y * sizeof(pixel_t)));
    info.horizontal_resolution = pos.x;
    info.vertical_resolution   = pos.y;
    view_init(view_time,info,running_thread()->pid);
    view_updown(view_time,1);

    view_fill(&view_time->info,make_color(0x20,0x20,0x20),0,0,view_time->info.horizontal_resolution,view_time->info.vertical_resolution);
    pos.x = time_x0;
    pos.y = time_y0;
    view_slide(view_time,pos);

    message_t msg;
    msg.type = TICK_GET_TICKS;
    send_recv(NR_BOTH,SRV_TICK,&msg);
    uint64_t last_ticks = msg.msg3.m3l1 + 100;
    while (1)
    {
        // get_time(&time);
        msg.type = TICK_GET_TICKS;
        send_recv(NR_BOTH,SRV_TICK,&msg);
        if (msg.msg3.m3l1 - last_ticks >= 100)
        {
            last_ticks = msg.msg3.m3l1;
            get_time(&time);
            pos.x = 0;
            pos.y = 0;
            sprintf(s,time_style,time.year,time.month,time.day,time.hour,time.minuet,time.second);
            view_fill(&view_time->info,make_color(0x20,0x20,0x20),0,0,view_time->info.horizontal_resolution,view_time->info.vertical_resolution);
            pr_ttf_str(&view_time->info,&pos,make_color(255,255,255),s,12.0);
            view_reflush(view_time,0,0,view_time->info.horizontal_resolution,view_time->info.vertical_resolution);
        }
    };
}

PRIVATE void make_background()
{
    intr_status_t intr_status = intr_disable();
    graph_info_t* graph_info = &bg->info;
    int tsk  = 70;
    pixel_t color;
    color.alpha = 0;
    color.red   = 0x20;
    color.green = 0x70;
    color.blue  = 0x90;
    view_fill(graph_info,color,0,0,graph_info->horizontal_resolution,
              graph_info->vertical_resolution - tsk);

    color.red   = 0x20;
    color.green = 0x20;
    color.blue  = 0x20;
    view_fill(graph_info,color,0,    graph_info->vertical_resolution - tsk,
              graph_info->horizontal_resolution,graph_info->vertical_resolution);

    color.red   = 0x50;
    color.green = 0x50;
    color.blue  = 0x50;
    view_fill(graph_info,color,0,    graph_info->vertical_resolution - tsk,
              tsk * 4,graph_info->vertical_resolution);

    color.red   = 0xa0;
    color.green = 0xa0;
    color.blue  = 0xa0;
    view_fill(graph_info,color,10,    graph_info->vertical_resolution - tsk + 10,
              tsk - 10,graph_info->vertical_resolution - 10);
    intr_set_status(intr_status);
}

PUBLIC void view_main()
{
    message_t msg;
    viewctl_init(&viewctl,g_boot_info.graph_info,1024);
    bg = view_alloc(&viewctl);
    graph_info_t bg_info;
    bg_info.frame_buffer_base =
        (uintptr_t)KADDR_P2V(pmalloc(
                                    viewctl.info.horizontal_resolution
                                    * viewctl.info.vertical_resolution
                                    * sizeof(pixel_t)));
    bg_info.horizontal_resolution = viewctl.info.horizontal_resolution;
    bg_info.vertical_resolution   = viewctl.info.vertical_resolution;
    view_init(bg,bg_info,running_thread()->pid);
    make_background();

    uint32_t* madt = (uint32_t*)KADDR_P2V(g_boot_info.madt_addr);
    char Signature[5];
    Signature[0] = (*madt >>  0) & 0xff;
    Signature[1] = (*madt >>  8) & 0xff;
    Signature[2] = (*madt >> 16) & 0xff;
    Signature[3] = (*madt >> 24) & 0xff;
    Signature[4] = 0;
    char s[128];
    sprintf(s,"MADT Signature: %s\n",Signature);
    position_t pos = {0,16};
    pixel_t color = make_color(255,255,255);
    color.alpha = 0;
    pr_ttf_str(&bg_info,&pos,color,s,16.0);

    view_updown(bg,0);
    thread_start("time",31,time_thread,NULL);

    while(1)
    {
        send_recv(NR_RECEIVE,ANY,&msg);
        switch(msg.type)
        {
            default:
                LOG_ERROR("unknow msg.type: %d\n",msg.type);
                break;
        }
    }
}

PRIVATE void viewctl_init(viewctl_t* ctl,graph_info_t info,int views)
{
    ctl->views = KADDR_P2V(pmalloc(sizeof(view_t*) * views));
    ctl->view0 = KADDR_P2V(pmalloc(sizeof(view_t) * views));
    int i;
    for (i = 0;i < views;i++)
    {
        ctl->view0[i].flag = 0;
        ctl->view0[i].ctl = ctl;
    }
    ctl->number_of_views = views;
    ctl->top = -1;
    ctl->info = info;
    ctl->map = KADDR_P2V(pmalloc(
                                sizeof(uintptr_t)
                                * info.horizontal_resolution
                                * info.vertical_resolution));
    LOG_INFO("views %p,view0 %p,number of views %d,map: %p\n",ctl->views,ctl->view0,ctl->number_of_views,ctl->map);
    return;
}

PRIVATE view_t* view_alloc(viewctl_t* ctl)
{
    view_t* view;
    uint32_t i;
    for (i = 0;i < ctl->number_of_views;i++)
    {
        if (ctl->view0[i].flag == 0)
        {
            view = &ctl->view0[i];
            view->flag = 1;
            view->height = -1;
            view->holder = NO_TASK;
            LOG_INFO("alloc view: %p,i = %d\n",view,i);
            return view;
        }
    }
    return NULL;
}

PRIVATE void view_init(view_t* view,graph_info_t info,pid_t holder)
{
    view->info = info;
    view->position.x = 0;
    view->position.y = 0;
    view->holder = holder;
}

PRIVATE void view_remove(view_t* view)
{
    if (view->height != -1)
    {
        view_updown(view,-1);
    }
    view->flag = 0;
    return;
}

PRIVATE void view_updown(view_t* view,int height)
{
    viewctl_t* ctl = view->ctl;
    unsigned int xsize = view->info.horizontal_resolution;
    unsigned int ysize = view->info.vertical_resolution;
    ASSERT(ctl != NULL)
    int i;
    int old_height = view->height;
    /* 修正高度 */
    if (height > (ctl->top + 1))
    {
        height = ctl->top + 1;
    }
    if (height < -1)
    {
        height = -1;
    }
    view->height = height;
    ASSERT(height >= -1);
    ASSERT(height <= ctl->top + 1);
    /* 开始排序 */
    /* 图层下降 */
    if (old_height > height)
    {
        if (height >= 0)
        {
            for (i = old_height;i > height;i--)
            {
                ctl->views[i] = ctl->views[i - 1];
                ctl->views[i]->height = i;
            }
            ctl->views[height] = view;
            /* 刷新 */
            view_reflushmap(ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,view->height + 1);
            view_reflushsub(ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,view->height + 1,old_height);
        }
        else
        {
            if (ctl->top > old_height)
            {
                for (i = old_height;i < ctl->top;i++)
                {
                    ctl->views[i] = ctl->views[i + 1];
                    ctl->views[i]->height = i;
                }
            }
            ctl->top--;
            view_reflushmap(ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,0);
            view_reflushsub(ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,0,old_height - 1);
        }
    }
    /* 图层升高 */
    else if (old_height < height)
    {
        if (old_height >= 0)
        {
            for (i = old_height;i < height;i++)
            {
                ctl->views[i] = ctl->views[i + 1];
                ctl->views[i]->height = i;
            }
            ctl->views[height] = view;
        }
        else
        {
            for (i = ctl->top;i >= height;i--)
            {
                ctl->views[i + 1] = ctl->views[i];
                ctl->views[i + 1]->height = i + 1;
            }
            ctl->views[height] = view;
            ctl->top++;
        }
        /* 刷新 */
        view_reflushmap(ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,height);
        view_reflushsub(ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,height,height);
    }
    return;
}


PRIVATE void view_slide(view_t* view,position_t pos)
{
    int x0 = view->position.x;
    int y0 = view->position.y;
    view->position = pos;
    unsigned int xsize = view->info.horizontal_resolution;
    unsigned int ysize = view->info.vertical_resolution;
    ASSERT(view->ctl != NULL);
    if (view->height >= 0)
    {
        view_reflushmap(view->ctl,x0,y0,x0 + xsize,y0 + ysize,0);
        view_reflushmap(view->ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,view->height);
        view_reflushsub(view->ctl,x0,y0,x0 + xsize,y0 + ysize,0,view->height - 1);
        view_reflushsub(view->ctl,view->position.x,view->position.y,view->position.x + xsize,view->position.y + ysize,view->height,view->height);
    }
    return;
}

PRIVATE void view_reflush(view_t* view,int x0,int y0,int x1,int y1)
{
    ASSERT(view->ctl != NULL);
    if (view->height >= 0)
    {
        view_reflushsub(view->ctl,view->position.x + x0,view->position.y + y0,view->position.x + x1,view->position.y + y1,view->height,view->height);
    }
    return;
}

PRIVATE void view_reflushsub(viewctl_t* ctl,int x0,int y0,int x1,int y1,int h0,int h1)
{
    int bx;
    int by;
    int bx0;
    int by0;
    int bx1;
    int by1;
    int x;
    int y;
    int h;
    view_t* view;
    int ctl_xsize = ctl->info.horizontal_resolution;
    int ctl_ysize = ctl->info.vertical_resolution;
    int view_xsize = 0;
    int view_ysize = 0;
    pixel_t* frame_buffer = (pixel_t*)ctl->info.frame_buffer_base;
    if (x0 < 0){ x0 = 0; }
    if (y0 < 0){ y0 = 0; }
    if (x1 > ctl_xsize){ x1 = ctl_xsize; }
    if (y1 > ctl_ysize){ y1 = ctl_ysize; }
    for (h = h0;h <= h1;h++)
    {
        view = ctl->views[h];
        view_xsize = view->info.horizontal_resolution;
        view_ysize = view->info.vertical_resolution;
        bx0 = x0 - view->position.x;
        by0 = y0 - view->position.y;
        bx1 = x1 - view->position.x;
        by1 = y1 - view->position.y;
        if (bx0 < 0){ bx0 = 0; }
        if (by0 < 0){ by0 = 0; }
        if (bx1 > view_xsize){ bx1 = view_xsize; }
        if (by1 > view_ysize){ by1 = view_ysize; }
        // page_dir_activate(pid2thread(view->holder));
        for (by = by0;by < by1;by++)
        {
            y = view->position.y + by;
            for (bx = bx0;bx < bx1;bx++)
            {
                x = view->position.x + bx;
                /* 根据map刷新 */
                if (ctl->map[y * (ctl_xsize) + x] == (uintptr_t)(view))
                {
                    pixel_t bgc = *(frame_buffer + y * ctl_xsize + x);
                    pixel_t color = *((pixel_t*)view->info.frame_buffer_base + by * view_xsize + bx);

                    uint8_t red = (color.red * (255 - color.alpha)
                          + bgc.red * color.alpha) / 255;
                    uint8_t green = (color.green * (255 - color.alpha)
                                    + bgc.green * color.alpha) / 255;
                    uint8_t blue = (color.blue * (255 - color.alpha)
                                    + bgc.blue * color.alpha) / 255;

                    *(frame_buffer + y * ctl_xsize + x) = make_color(red,green,blue);
                }
            }
        }
        // page_dir_activate(running_thread());
    }
    return;
}

PRIVATE void view_reflushmap(viewctl_t* ctl,int x0,int y0,int x1,int y1,int h0)
{
    int bx;
    int by;
    int bx0;
    int by0;
    int bx1;
    int by1;
    int x;
    int y;
    int h;
    view_t* view;
    int ctl_xsize = ctl->info.horizontal_resolution;
    int ctl_ysize = ctl->info.vertical_resolution;
    int view_xsize = 0;
    int view_ysize = 0;
    if (x0 < 0){ x0 = 0; }
    if (y0 < 0){ y0 = 0; }
    if (x1 > ctl_xsize){ x1 = ctl_xsize; }
    if (y1 > ctl_ysize){ y1 = ctl_ysize; }

    for (h = h0;h <= (ctl->top);h++)
    {
        view = ctl->views[h];
        view_xsize = view->info.horizontal_resolution;
        view_ysize = view->info.vertical_resolution;
        bx0 = x0 - view->position.x;
        by0 = y0 - view->position.y;
        bx1 = x1 - view->position.x;
        by1 = y1 - view->position.y;
        if (bx0 < 0){ bx0 = 0; }
        if (by0 < 0){ by0 = 0; }
        if (bx1 > view_xsize){ bx1 = view_xsize; }
        if (by1 > view_ysize){ by1 = view_ysize; }
        for (by = by0;by < by1;by++)
        {
            y = (view->position.y) + by;
            for (bx = bx0;bx < bx1;bx++)
            {
                x = view->position.x + bx;
                ctl->map[y * ctl_xsize + x] = (uintptr_t)view;
            }
        }
    }
    return;
}
